﻿using Sidvall.Business;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;

namespace Sidvall.Data
{
    public class ServiceDataRowCollection<TEntity> : IList, IList<TEntity>,
        IDataRowProvider, IDataRowProvider<TEntity>, Sidvall.Data.ITotalRowCount, Sidvall.Data.IListContextProvider, IBusinessCollectionSettingsProvider, INotifyChildChanged
        where TEntity : ServiceDataRowBase
    {
        #region Public Members

        public event System.EventHandler<ChildChangedEventArgs> ChildChanged;

        #region Count

        public int Count
        {
            get
            {
                return this.Items.Count;
            }
        }

        #endregion
        #region IsFixedSize

        public bool IsFixedSize
        {
            get
            {
                return false;
            }
        }

        #endregion
        #region IsSynchronized

        public bool IsSynchronized
        {
            get
            {
                return false;
            }
        }

        #endregion
        #region SyncRoot

        public object SyncRoot
        {
            get
            {
                return null;
            }
        }

        #endregion
        #region this

        public TEntity this[int index]
        {
            get
            {
                return this.Items[index];
            }
            set
            {
                var item = this.Items[index];
                RemoveEventHooks(item);
                DeleteChild(item);
                this.Items[index] = value;
            }
        }

        #endregion

        // Protected
        protected internal long? TotalRowCount { get; set; }
        #region BusinessSettings

        private System.Lazy<BusinessCollectionSettings> _BusinessSettings = new System.Lazy<BusinessCollectionSettings>(BusinessCollectionSettings.CreateSettingsNone);
        protected BusinessCollectionSettings BusinessSettings
        {
            get
            {
                return _BusinessSettings.Value;
            }
        }

        #endregion
        #region DeletedItemKeys

        private List<string> _DeletedItemKeys;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        protected internal List<string> DeletedItemKeys
        {
            get
            {
                if (_DeletedItemKeys == null)
                    _DeletedItemKeys = new List<string>();
                return _DeletedItemKeys;
            }
            set
            {
                _DeletedItemKeys = value;
            }
        }

        #endregion
        #region IsReadOnly

        protected bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        #endregion
        #region Items

        private List<TEntity> _Items;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        protected internal List<TEntity> Items
        {
            get
            {
                if (_Items == null)
                    _Items = new List<TEntity>();
                return _Items;
            }
            set
            {
                _Items = value;
            }
        }

        #endregion

        #region Add

        public int Add(object value)
        {
            var item = value as TEntity;
            if (item != null)
            {
                Add(item);
                return this.Items.Count - 1;
            }
            return -1;
        }
        public void Add(TEntity item)
        {
            this.Items.Add(item);
            AddChild(item);
        }

        #endregion
        #region AddRange

        public void AddRange(IEnumerable<TEntity> items)
        {
            if (items == null)
                return;
            foreach (var item in items)
            {
                if (item != null)
                    Add(item);
            }
        }

        #endregion
        #region Clear

        public void Clear()
        {
            foreach (var item in this.Items)
            {
                RemoveEventHooks(item);
                DeleteChild(item);
            }
            this.Items.Clear();
        }

        #endregion
        #region Contains

        public bool Contains(object value)
        {
            var item = value as TEntity;
            if (item != null)
                return Contains(item);
            return false;
        }
        public bool Contains(TEntity item)
        {
            return this.Items.Contains(item);
        }

        #endregion
        #region CopyTo

        public void CopyTo(System.Array array, int index)
        {
            throw new System.NotImplementedException();
        }
        public void CopyTo(TEntity[] array, int arrayIndex)
        {
            this.Items.CopyTo(array, arrayIndex);
        }

        #endregion
        #region IndexOf

        public int IndexOf(object value)
        {
            var item = value as TEntity;
            if (item != null)
                return IndexOf(item);
            return -1;
        }
        public int IndexOf(TEntity item)
        {
            return this.Items.IndexOf(item);
        }

        #endregion
        #region Insert

        public void Insert(int index, object value)
        {
            var item = value as TEntity;
            if (item != null)
                Insert(index, item);
        }
        public void Insert(int index, TEntity item)
        {
            this.Items.Insert(index, item);
            AddChild(item);
        }

        #endregion
        #region Remove

        public void Remove(object value)
        {
            var item = value as TEntity;
            if (item != null)
                Remove(item);
        }
        public bool Remove(TEntity item)
        {
            var result = this.Items.Remove(item);
            if (result)
            {
                RemoveEventHooks(item);
                DeleteChild(item);
            }
            return result;
        }

        #endregion
        #region RemoveAt

        public void RemoveAt(int index)
        {
            var item = this.Items[index];
            RemoveEventHooks(item);
            DeleteChild(item);
            this.Items.RemoveAt(index);
        }

        #endregion

        // Protected
        #region GetEnumerator

        protected IEnumerator<TEntity> GetEnumerator()
        {
            return this.Items.GetEnumerator();
        }

        #endregion
        #region OnChildChanged

        protected void OnChildChanged(object child, PropertyChangedEventArgs childArgs)
        {
            if (this.BusinessSettings.EnabledBusinessCollectionEvents.HasFlag(BusinessCollectionEvents.NotifyChildChanged))
            {
                var args = new ChildChangedEventArgs(child, childArgs);
                OnChildChanged(args);
            }
        }
        protected void OnChildChanged(object child, NotifyCollectionChangedEventArgs childArgs)
        {
            if (this.BusinessSettings.EnabledBusinessCollectionEvents.HasFlag(BusinessCollectionEvents.NotifyChildChanged))
            {
                var args = new ChildChangedEventArgs(child, childArgs);
                OnChildChanged(args);
            }
        }
        protected void OnChildChanged(object child, ChildChangedEventArgs e)
        {
            if (this.BusinessSettings.EnabledBusinessCollectionEvents.HasFlag(BusinessCollectionEvents.NotifyChildChanged))
                OnChildChanged(e);
        }
        protected virtual void OnChildChanged(ChildChangedEventArgs args)
        {
            if (this.ChildChanged != null)
                this.ChildChanged.Invoke(this, args);
        }

        #endregion

        #region Interface Members

        BusinessCollectionSettings IBusinessCollectionSettingsProvider.BusinessSettings
        {
            get
            {
                return this.BusinessSettings;
            }
        }
        IEnumerable IDataRowProvider.DataItems
        {
            get
            {
                return this;
            }
        }
        IEnumerable<TEntity> IDataRowProvider<TEntity>.DataItems
        {
            get
            {
                return this;
            }
        }
        long ITotalRowCount.TotalRowCount
        {
            get
            {
                if (this.TotalRowCount != null)
                    return this.TotalRowCount.Value;
                return this.Count;
            }
            set
            {
                this.TotalRowCount = value;
            }
        }
        IEnumerable<string> IListContextProvider.DeletedItemKeys
        {
            get
            {
                return this.DeletedItemKeys;
            }
            set
            {
                this.DeletedItemKeys = new List<string>(value);
            }
        }
        bool ICollection<TEntity>.IsReadOnly
        {
            get
            {
                return this.IsReadOnly;
            }
        }
        IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator()
        {
            return GetEnumerator();
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        bool IList.IsReadOnly
        {
            get
            {
                return this.IsReadOnly;
            }
        }
        object IList.this[int index]
        {
            get
            {
                return this[index];
            }
            set
            {
                var item = value as TEntity;
                if (item != null)
                    this[index] = item;
            }
        }

        #endregion

        #endregion
        #region Private Members

        #region AddChild

        private void AddChild(TEntity item)
        {
            var dataRowContextProvider = item as IDataRowContextProvider;
            if ((dataRowContextProvider.DataRowContextStorageKey != null) && (this.DeletedItemKeys.Contains(dataRowContextProvider.DataRowContextStorageKey)))
            {
                if (dataRowContextProvider.DataRowStateIsNew)
                    dataRowContextProvider.SetDataRowContext(null, DataRowState.Modified);
                this.DeletedItemKeys.Remove(dataRowContextProvider.DataRowContextStorageKey);
            }
            AddEventHooks(item);
        }

        #endregion
        #region DeleteChild

        private void DeleteChild(IDataRowContextProvider item)
        {
            if (!this.BusinessSettings.MarkAsDeleted)
                return;
            if (item?.DataRowContextStorageKey == null)
                return;
            if (item.DataRowStateIsNew)
                return;
            this.DeletedItemKeys.Add(item.DataRowContextStorageKey);
        }

        #endregion
        #region AddEventHooks

        private void AddEventHooks(TEntity item)
        {
            if (item == null)
                return;
            var pc = item as INotifyPropertyChanged;
            if (pc != null)
                pc.PropertyChanged += OnChildChanged;
            var ncc = item as INotifyCollectionChanged;
            if (ncc != null)
                ncc.CollectionChanged += OnChildChanged;
            var cc = item as INotifyChildChanged;
            if (cc != null)
                cc.ChildChanged += OnChildChanged;
        }

        #endregion
        #region RemoveEventHooks

        private void RemoveEventHooks(object item)
        {
            if (item == null)
                return;
            var pc = item as INotifyPropertyChanged;
            if (pc != null)
                pc.PropertyChanged -= OnChildChanged;
            var ncc = item as INotifyCollectionChanged;
            if (ncc != null)
                ncc.CollectionChanged -= OnChildChanged;
            var cc = item as INotifyChildChanged;
            if (cc != null)
                cc.ChildChanged -= OnChildChanged;
        }

        #endregion

        #endregion
        #region Constructors

        public ServiceDataRowCollection()
        {
        }
        public ServiceDataRowCollection(IEnumerable<TEntity> items)
        {
            if (items == null)
                return;
            this.Items = new List<TEntity>(items);
        }
        protected ServiceDataRowCollection(BusinessCollectionEvents enabledBusinessCollectionEvents)
        {
            this.BusinessSettings.EnabledBusinessCollectionEvents = enabledBusinessCollectionEvents;
        }
        protected ServiceDataRowCollection(IEnumerable<TEntity> items, BusinessCollectionEvents enabledBusinessCollectionEvents)
            : this(items)
        {
            this.BusinessSettings.EnabledBusinessCollectionEvents = enabledBusinessCollectionEvents;
        }

        #endregion
    }
}
